package cn.thoughtworks.school.programCenter.services;

import cn.thoughtworks.school.programCenter.entities.*;
import cn.thoughtworks.school.programCenter.entities.dto.AssignmentQuizData;
import cn.thoughtworks.school.programCenter.exceptions.BusinessException;
import cn.thoughtworks.school.programCenter.feign.OrganizationCenterFeign;
import cn.thoughtworks.school.programCenter.repositories.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hankcs.hanlp.collection.sequence.SString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.thoughtworks.school.programCenter.entities.Operation.*;

@Service
@Slf4j
public class ProgramService {


    @Autowired
    private ReviewQuizRepository reviewQuizRepository;
    @Autowired
    private OrganizationCenterFeign organizationCenterFeign;
    @Autowired
    private TaskRepository taskRepository;
    @Autowired
    private UserProgramRepository userProgramRepository;
    @Autowired
    private TutorProgramRepository tutorProgramRepository;
    @Autowired
    private UserCenterService userCenterService;
    @Autowired
    private ProgramRepository programRepository;
    @Autowired
    private TopicRepository topicRepository;
    @Autowired
    private AssignmentRepository assignmentRepository;
    @Autowired
    private AssignmentQuizRepository assignmentQuizRepository;
    @Autowired
    private FollowRepository followRepository;
    @Autowired
    private OrganizationService organizationService;
    @Autowired
    private QuizCenterService quizCenterService;
    @Autowired
    private AssignmentService assignmentService;
    @Autowired
    private DiffService diffService;

    private ObjectMapper oMapper = new ObjectMapper();

    @Cacheable(value = "LEADER_BOARD_CACHE")
    public List getStudentLeaderBoard() {
        return cacheLeaderBoard();
    }

    public List cacheLeaderBoard() {
        List result = new ArrayList();
        List<Program> programs = programRepository.findAllByAvailableIsTrue();
        programs.forEach(program -> {
            Map temp = new HashMap();
            List leaderBoards = new ArrayList();
            List<Task> tasks = taskRepository.findByProgramId(program.getId());
            List<Long> ids = getTaskIds(tasks);
            List<UserProgram> userPrograms = userProgramRepository.findByProgramId(program.getId());

            userPrograms.forEach(item -> {
                log.info(String.format("userId:" + item.getUserId() + ",programId:" + item.getProgramId()));
                ResponseEntity userInfoResponse = userCenterService.getUserInfo(item.getUserId());
                if (Objects.nonNull(userInfoResponse)) {
                    Map userInfo = (Map) userInfoResponse.getBody();
                    String name = getStudentName(userInfo);
                    Long studentId = getStudentId(userInfo);
                    leaderBoards.add(getUserBoard(ids, studentId, name));
                }
            });

            leaderBoards.sort((Comparator<Map>) (o1, o2) ->
                (int) (Double.valueOf(o2.get("finishHomeworkPoint").toString()) - Double.valueOf(o1.get("finishHomeworkPoint").toString())));
            temp.put("currentProgramLeaderBoard", leaderBoards);
            temp.put("programId", program.getId());
            result.add(temp);
        });
        return result;
    }

    public List getLeaderBoardByProgramId(Long programId) {
        List leaderBoards = new ArrayList();
        List<Task> tasks = taskRepository.findByProgramId(programId);
        List<Long> taskIdList = tasks.stream().map(Task::getId).collect(Collectors.toList());

        List<UserProgram> userPrograms = userProgramRepository.findByProgramId(programId);
        List<Long> userIdList = userPrograms.stream().map(UserProgram::getUserId).collect(Collectors.toList());

        List<Map> scoresAndUsers = reviewQuizRepository.getScoresLimit(userIdList, taskIdList);
        List<Long> studentIds = scoresAndUsers.stream().map(scoresAndUser -> Long.parseLong(scoresAndUser.get("studentId").toString())).collect(Collectors.toList());

        String userIds = scoresAndUsers.stream().map(scoresAndUsersLimit -> scoresAndUsersLimit.get("studentId").toString()).collect(Collectors.joining(","));
        List users = userCenterService.getUsersByIds(userIds);
        List<Map> finishSectionCountTop10 = reviewQuizRepository.getFinishSectionCountLimit(studentIds, taskIdList);

        users.forEach(user -> {
            Map userMap = oMapper.convertValue(user, Map.class);
            String name = getStudentName(userMap);
            Long studentId = getStudentId(userMap);
            leaderBoards.add(getUserBoardTop10(scoresAndUsers, finishSectionCountTop10, studentId, name));
        });

        leaderBoards.sort((Comparator<Map>) (o1, o2) ->
            (int) (Double.parseDouble(o2.get("finishHomeworkPoint").toString()) - Double.parseDouble(o1.get("finishHomeworkPoint").toString())));
        return leaderBoards;
    }

    public Map getUserBoardTop10(List<Map> scoresAndUsersLimitTop10, List<Map> finishSectionCountTop10, Long studentId, String name) {
        Map result = new HashMap();

        result.put("name", name);
        result.put("studentId", studentId);
        Optional<Map> scoreAndUser = scoresAndUsersLimitTop10.stream().filter(scoresAndUsersLimit -> Objects.equals(scoresAndUsersLimit.get("studentId").toString(), studentId.toString())).findFirst();
        result.put("finishHomeworkPoint", scoreAndUser.isPresent() ? scoreAndUser.get().get("grade") : 0);
        Optional<Map> finishSection = finishSectionCountTop10.stream().filter(finishSectionCount -> Objects.equals(finishSectionCount.get("studentId").toString(), studentId.toString())).findFirst();
        result.put("finishHomeworkCount", finishSection.isPresent() ? finishSection.get().get("count") : 0);
        return result;
    }


    private Long getStudentId(Map userInfo) {
        if (Objects.isNull(userInfo.get("id"))) {
            return Long.valueOf(userInfo.get("userId").toString());
        } else {
            return Long.valueOf(userInfo.get("id").toString());
        }
    }

    private String getStudentName(Map userInfo) {
        if (Objects.isNull(userInfo.get("name"))) {
            return userInfo.get("userName").toString();
        } else {
            return userInfo.get("name").toString();
        }
    }

    public Map getUserBoard(List<Long> taskIds, Long studentId, String name) {
        Map result = new HashMap();

        result.put("name", name);
        result.put("studentId", studentId);
        if (taskIds.isEmpty()) {
            result.put("finishHomeworkCount", 0);
            result.put("finishHomeworkPoint", 0);
        } else {
            Long finishSectionCount = reviewQuizRepository.getFinishSectionCount(studentId, taskIds);
            result.put("finishHomeworkCount", Objects.isNull(finishSectionCount) ? 0 : finishSectionCount);
            Long scores = reviewQuizRepository.getScores(studentId, taskIds);
            result.put("finishHomeworkPoint", Objects.isNull(scores) ? 0 : scores);
        }
        return result;
    }

    private List<Long> getTaskIds(List<Task> tasks) {
        List<Long> result = new ArrayList<>();
        for (Task task : tasks) {
            result.add(task.getId());
        }

        return result;
    }

    @Transactional
    public void copyProgramToCurrentOrganization(Long programId, User current) throws BusinessException {
        Program program = programRepository.findById(programId).orElseThrow(() -> new BusinessException("训练营不存在"));
        copyProgram(program, current, program.getOrganizationId());
    }

    @Transactional
    public void copyProgramToOrganization(Long organizationId, Long programId, User current) throws BusinessException {
        Program program = programRepository.findById(programId).orElseThrow(() -> new BusinessException("训练营不存在"));
        Program duplicationProgram = copyProgram(program, current, organizationId);

        List<Assignment> assignments = assignmentService.findByProgramId(duplicationProgram.getId());
        List<AssignmentQuizData> assignmentQuizData = getCurrentProgramQuizzes(assignments, organizationId, current.getId());
        List<AssignmentQuizData> newAssignmentQuizData = quizCenterService.copyQuizzesToOrganization(assignmentQuizData);
        updateAssignmentQuiz(newAssignmentQuizData, assignments,current.getId());
    }

    private void updateAssignmentQuiz(List<AssignmentQuizData> newAssignmentQuizData, List<Assignment> assignments,Long userId) {
        assignments.forEach(assignment -> {
            List<AssignmentQuiz> assignmentQuizzes = assignment.getSelectedQuizzes().stream().map(quiz -> {
                AssignmentQuizData assignmentQuizData = newAssignmentQuizData.stream()
                    .filter(quizData -> quizData.getAssignmentId().equals(assignment.getId()) &&
                        quiz.getQuizId().equals(quizData.getQuizId()))
                    .findFirst().get();
                quiz.setQuizId(assignmentQuizData.getNewQuizId());
                return quiz;
            }).collect(Collectors.toList());
            assignment.setSelectedQuizzes(assignmentQuizzes);
        });
        assignmentRepository.saveAll(assignments);
        diffService.copyAssignmentsNotification(assignments,userId,CREATE);
    }

    private List<AssignmentQuizData> getCurrentProgramQuizzes(List<Assignment> assignments, Long organizationId, Long creatorId) {
        return AssignmentQuizData.build(assignments, organizationId, creatorId);
    }

    public Program copyProgram(Program program, User current, Long organizationId) throws BusinessException {
        Program duplicationProgram = copyProgramProcess(program, current, organizationId);
        List<Topic> topics = topicRepository.findByProgramId(program.getId());
        List<Task> tasks = taskRepository.findByProgramId(program.getId());

        for (Topic topic : topics) {
            List<Task> currentTopicTasks = tasks.stream().filter(task -> task.getTopicId().equals(topic.getId())).collect(Collectors.toList());
            Topic duplicationTopic = copyTopicsProcess(topic, duplicationProgram, current.getId());
            copyTasksProcess(duplicationProgram, duplicationTopic, currentTopicTasks, current.getId());
        }
        return duplicationProgram;
    }

    private List<Task> copyTasksProcess(Program program, Topic topic, List<Task> tasks, Long userId) throws BusinessException {
        List<Task> result = new ArrayList<>();
        for (Task task : tasks) {
            Task duplicationTask = task.copy(program, topic, task);
            taskRepository.save(duplicationTask);
            diffService.copyTaskNotification(duplicationTask, task.getId(), userId, COPY);
            copyAssignmentsProcess(task, duplicationTask,userId);
            result.add(duplicationTask);
        }
        return result;
    }

    private void copyAssignmentsProcess(Task task, Task duplicationTask, Long userId) throws BusinessException {
        List<Assignment> assignments = task.getAssignments();
        for (Assignment assignment : assignments) {
            Assignment duplicationAssignment = assignment.copyAssignment(assignment, duplicationTask);
            assignmentRepository.save(duplicationAssignment);
            diffService.updateAssignmentNotification(duplicationAssignment.getId(), userId, CREATE);
        }
    }

    private Topic copyTopicsProcess(Topic topic, Program program, Long userId) {
        Topic duplicationTopic = topic.copy(topic, program);
        duplicationTopic = topicRepository.save(duplicationTopic);
        diffService.copyTopicNotification(duplicationTopic,topic.getId(), userId, CREATE);
        return duplicationTopic;
    }

    private Program copyProgramProcess(Program program, User current, Long organizationId) {
        Program duplicationProgram = program.copy(program, current.getId(), organizationId);
        duplicationProgram = programRepository.save(duplicationProgram);
        diffService.copyProgramNotification(duplicationProgram, program.getId(), current.getId(), COPY);
        return duplicationProgram;
    }

    @Transactional
    public void copyTaskToTopic(Long organizationId, List<Long> sourceTaskIds, Long topicId, User current) throws BusinessException {
        Long currentOrganizationId = userCenterService.getUserCurrentOrganizationId(current.getId());

        Topic topic = topicRepository.findById(topicId).orElseThrow(() -> new BusinessException("topic 不存在"));
        Program program = programRepository.findById(topic.getProgramId()).orElseThrow(() -> new BusinessException("program 不存在"));
        List<Task> tasks = taskRepository.findAllById(sourceTaskIds);

        List<Task> duplicationTasks = copyTasksProcess(program, topic, tasks, current.getId());
        if (isCopyToAnotherOrganization(organizationId, currentOrganizationId)) {
            List<Long> taskIds = duplicationTasks.stream().map(Task::getId).collect(Collectors.toList());
            List<Assignment> assignments = assignmentRepository.findByTaskIds(taskIds);
            List<AssignmentQuizData> assignmentQuizData = getCurrentProgramQuizzes(assignments, organizationId, current.getId());
            List<AssignmentQuizData> newAssignmentQuizData = quizCenterService.copyQuizzesToOrganization(assignmentQuizData);
            updateAssignmentQuiz(newAssignmentQuizData, assignments,current.getId());
        }
    }

    private boolean isCopyToAnotherOrganization(Long organizationId, Long currentOrganizationId) {
        return !organizationId.equals(currentOrganizationId);
    }

    public void moveTaskToTopic(List<Long> sourceTaskIds, Long topicId, Long userId) throws BusinessException {
        Topic topic = topicRepository.findById(topicId).orElseThrow(() -> new BusinessException("topic 不存在"));
        Program program = programRepository.findById(topic.getProgramId()).orElseThrow(() -> new BusinessException("program 不存在"));
        taskRepository.moveTasksToTopic(sourceTaskIds, program.getId(), topicId);
        diffService.moveTaskNotification(sourceTaskIds, topicId, userId, MOVE);
    }

    public List searchStudentsPrograms(List<Map> students) {
        List result = students.stream().map(student -> {
            List<Program> programs = programRepository.getProgramsByUserProgramStudentId(Long.parseLong(student.get("id").toString()));
            student.put("programs", programs);
            return student;
        }).collect(Collectors.toList());
        return result;
    }

    public List searchTutorsPrograms(List<Map> tutors) {
        return tutors.stream().map(tutor -> {
            List<Program> programs = programRepository.getProgramsByUserProgramTutorId(Long.parseLong(tutor.get("id").toString()));
            tutor.put("programs", programs);
            return tutor;
        }).collect(Collectors.toList());
    }

    public void updateRegisterProgramLink(Map data, Long userId) throws BusinessException {
        String type = data.get("type").toString();
        Long programId = Long.parseLong(data.get("programId").toString());
        boolean enable = (boolean) data.get("enable");
        Program program = programRepository.findById(programId).orElseThrow(() -> new BusinessException("训练营不存在"));
        if ("tutor".equals(type)) {
            program.setTutorLink(enable);
        }
        if ("student".equals(type)) {
            program.setStudentLink(enable);
        }
        programRepository.save(program);
        diffService.updateProgramNotification(programId, userId, UPDATE);
    }

    public boolean isInStudentProgram(Long id, Long userId) {
        if (isExpired(id)) return false;
        return userProgramRepository.findByProgramIdAndUserId(id, userId) != null;
    }

    private boolean isExpired(Long id) {
        Optional<Program> program = programRepository.findById(id);
        if (!program.isPresent() || program.get().isExpired()) {
            return true;
        }
        return false;
    }

    public boolean isInTutorProgram(Long id, Long tutorId) {
        Integer REVIEWER_ROLE = 3;
        Map user = (Map) userCenterService.getUserInfo(tutorId).getBody();
        List<Integer> roles = (List<Integer>) (user.get("roles"));
        if (isExpired(id) && !roles.contains(REVIEWER_ROLE)) {
            return false;
        }
        return tutorProgramRepository.findByTutorIdAndProgramId(tutorId, id) != null;
    }

    public Follow followedByInviteLink(String code, User current) throws BusinessException {
        String[] followerInfo = convertCodeToFollowerInfo(code);
        if (!checkFormat(followerInfo, current)) {
            throw new BusinessException("Format is invalid");
        }
        Long tutorId = convertTutorId(followerInfo);
        Long programId = convertProgramId(followerInfo);
        if (alreadyFollowed(programId, tutorId, current)) {
            throw new BusinessException("Already followed.");
        }

        return followRepository.save(new Follow(programId, tutorId, current.getId()));
    }

    private boolean alreadyFollowed(Long programId, Long tutorId, User current) {
        Follow follow = followRepository.findByProgramIdAndTutorIdAndStudentId(programId, tutorId, current.getId());

        return Objects.nonNull(follow);
    }

    private boolean tutorAndStudentHasProgramPermission(String[] followerInfo, User current) {
        Long tutorId = convertTutorId(followerInfo);
        Long programId = convertProgramId(followerInfo);

        return isInStudentProgram(programId, current.getId()) && isInTutorProgram(programId, tutorId);
    }

    private Long convertProgramId(String[] followerInfo) {
        return Long.parseLong(followerInfo[2]);
    }

    private Long convertTutorId(String[] followerInfo) {
        String threeTimesTutorId = followerInfo[1];
        return Long.parseLong(threeTimesTutorId.substring(0, threeTimesTutorId.length() / 3));
    }

    private String[] convertCodeToFollowerInfo(String code) {
        return code.split("\\.");
    }

    private boolean checkFormat(String[] followerInfo, User current) {
        return followerInfo.length == 3 && tutorAndStudentHasProgramPermission(followerInfo, current);
    }

    public void removeStudentFromProgram(Long programId, Long studentId) {
        userProgramRepository.deleteByProgramIdAndStudentId(programId, studentId);
    }

    public void removeTutorFromProgram(Long programId, Long tutorId) {
        tutorProgramRepository.deleteByProgramIdAndTutorId(programId, tutorId);
    }

    public Program findProgramByIdAndOrganization(Long programId, User current) throws BusinessException {
        Long organizationId = userCenterService.getUserCurrentOrganizationId(current.getId());
        return programRepository.findByOrganizationIdAndIdAndAvailableIsTrue(organizationId, programId)
            .orElseThrow(() -> new BusinessException("Current program is not exist!"));
    }

    public List<Long> addUsersToProgram(Long programId, List<Long> userIds) throws BusinessException {
        List<UserProgram> existRecords = userProgramRepository.findByProgramIdAndUserIdIn(programId, userIds);
        List<Long> existIds = existRecords.stream().map(UserProgram::getUserId).collect(Collectors.toList());

        List<Long> tobeAdded = new ArrayList<>();
        for (Long userId : userIds) {
            if (existIds.contains(userId)) continue;
            UserProgram record = new UserProgram(userId, programId);
            userProgramRepository.save(record);
            tobeAdded.add(userId);
        }
        addUsersToOrganization(programId, userIds);
        return tobeAdded;
    }

    private void addUsersToOrganization(Long programId, List<Long> userIds) throws BusinessException {
        Program program = programRepository.findById(programId)
            .orElseThrow(() -> new BusinessException("current program is not exist!"));
        organizationCenterFeign.addUsersToOrganization(program.getOrganizationId(), userIds);
    }

    public List<Long> addUserToTutorsProgram(Long programId, List<Long> userIds) throws BusinessException {
        List<TutorProgram> existRecords = tutorProgramRepository.findByProgramIdAndTutorIdIn(programId, userIds);
        List<Long> existIds = existRecords.stream().map(TutorProgram::getTutorId).collect(Collectors.toList());

        List<Long> tobeAdded = new ArrayList<>();
        for (Long userId : userIds) {
            if (existIds.contains(userId)) continue;
            TutorProgram record = new TutorProgram(userId, programId);
            tutorProgramRepository.save(record);
            userCenterService.addTutorRole(record);
            tobeAdded.add(userId);
        }
        addUsersToOrganization(programId, userIds);
        return tobeAdded;

    }

    public void tutorJoinProgram(TutorProgram tutorProgram) throws BusinessException {
        tutorProgramRepository.save(tutorProgram);
        userCenterService.addTutorRole(tutorProgram);
        Program program = programRepository.findById(tutorProgram.getProgramId()).orElseThrow(
            () -> new BusinessException("Current program is not exist."));
        organizationService.addUserOrganization(program.getOrganizationId(), tutorProgram.getTutorId());
    }

    public void studentJoinProgram(UserProgram userProgram) throws BusinessException {
        userProgramRepository.save(userProgram);
        Program program = programRepository.findById(userProgram.getProgramId()).orElseThrow(
            () -> new BusinessException("Current program is not exist."));
        organizationService.addUserOrganization(program.getOrganizationId(), userProgram.getUserId());
    }

    public Map getProgramInviteLinks(Long programId) throws BusinessException {
        Map result = new HashMap<>();
        Program program = programRepository.findById(programId).orElseThrow(() -> new BusinessException("Current program is not exist."));
        result.put("studentLink", program.isStudentLink());
        result.put("tutorLink", program.isTutorLink());

        return result;
    }

    public Program createProgram(Program newProgram, User current) throws BusinessException {
        Long organizationId = userCenterService.getUserCurrentOrganizationId(current.getId());
        newProgram.setCreatorId(current.getId());
        newProgram.setOrganizationId(organizationId);
        Program program = programRepository.save(newProgram);
        diffService.updateProgramNotification(program.getId(), current.getId(), CREATE);
        return program;
    }

    public List<Program> getProgramsAndSubTopics(User user) {
        List<Long> organizationIds = organizationService.getOrganizationsByUserId(user.getId())
            .stream().map(Organization::getId).collect(Collectors.toList());

        List<Program> programs = programRepository.findAllByAvailableIsTrueAndOrganizationIdIn(organizationIds);
        List<Topic> topics = topicRepository.findAll();

        return programs.stream().map(program -> {
            List<Topic> subTopics = topics.stream().filter(topic ->
                Objects.equals(topic.getProgramId(), program.getId())).collect(Collectors.toList());
            program.setTopics(subTopics);
            return program;
        }).collect(Collectors.toList());
    }

    public Program findById(Long programId) throws BusinessException {
        return programRepository.findById(programId).orElseThrow(
            () -> new BusinessException("Current program is not exist."));
    }

    public PageImpl getTutorProgramPage(User current, Pageable pageable) {
        Long currentOrganizationId = userCenterService.getUserCurrentOrganizationId(current.getId());
        Integer REVIEWER_ROLE = 3;
        Map user = (Map) userCenterService.getUserInfo(current.getId()).getBody();
        List<Integer> roles = (List<Integer>) (user.get("roles"));

        List<Long> programIds = tutorProgramRepository.findByTutorId(current.getId()).stream()
            .map(TutorProgram::getProgramId)
            .collect(Collectors.toList());
        Page<Program> programPage;
        if (roles.contains(REVIEWER_ROLE)) {
            programPage = programRepository.findByAvailableIsTrueAndOrganizationIdAndIdIn(currentOrganizationId, programIds, pageable);
        } else {
            programPage = programRepository.findByAvailableIsTrueAndOrganizationIdAndIsExpiredIsFalseAndIdIn(currentOrganizationId, programIds, pageable);
        }

        List<UserProgram> allProgramStudents = userProgramRepository.findByProgramIdIn(programIds);
        List<Follow> allCurrentProgramFollowStudents = followRepository.findByProgramIdInAndTutorId(programIds, current.getId());
        List<Map> programs = programPage.stream().map(program -> {
            Map temp = new HashMap();

            List<UserProgram> programStudents = allProgramStudents.stream().filter(allProgramStudent -> allProgramStudent.getProgramId().equals(program.getId())).collect(Collectors.toList());
            List<Follow> currentProgramFollowStudents = allCurrentProgramFollowStudents.stream().filter(currentProgramFollowStudent -> currentProgramFollowStudent.getProgramId().equals(program.getId())).collect(Collectors.toList());
            temp.put("program", program);
            temp.put("totalStudentsCount", programStudents.size());
            temp.put("followStudentsCount", currentProgramFollowStudents.size());
            return temp;
        }).collect(Collectors.toList());

        return new PageImpl<>(programs, pageable, programPage.getTotalElements());
    }

    public void deleteProgram(Long programId, Long userId) throws BusinessException {
        Program program = programRepository.findById(programId).get();
        program.setAvailable(false);
        programRepository.save(program);
        diffService.updateProgramNotification(programId, userId, DELETE);
    }

    public List<Program> findByOrganizationId(Long organizationId) {
        return programRepository.findByOrganizationId(organizationId);
    }

    public Map getMyProgramsTopicsTasks(Long id) {
        Map<String, List> result = new HashMap<>();

        List<Long> programIds = userProgramRepository.findByUserIdAndProgramIdIsTrue(id)
            .stream().map(UserProgram::getProgramId)
            .collect(Collectors.toList());
        result.put("programs", programRepository.findByIdIn(programIds));
        result.put("topics", topicRepository.findByProgramIdInAndVisibleIsTrueOrderByOrderNumberAsc(programIds));
        result.put("tasks", taskRepository.findByProgramIdInAndVisibleIsTrueOrderByOrderNumberAsc(programIds));
        return result;

    }

    public void update(Program program, Long userId) throws BusinessException {
        programRepository.save(program);
        diffService.updateProgramNotification(program.getId(), userId, UPDATE);
    }

    public List<Program> findAll() {
        return programRepository.findAll();
    }

    public Map pullStudentPrograms(Long userId){
        Map<String, Object> allInfoCache = new HashMap<>();
        List<UserProgram> userPrograms = userProgramRepository.findByUserIdAndProgramIdIsAvailableAndNotIsExpired(userId);
        allInfoCache.put("userPrograms", userPrograms);
        List<Long> programIds = userPrograms.stream().map(UserProgram::getProgramId).collect(Collectors.toList());
        List<Program> programs = programRepository.findAllById(programIds);
        allInfoCache.put("programs", programs);
        List<Topic> topics = topicRepository.findByProgramIdInAndVisibleIsTrueOrderByOrderNumberAsc(programIds);
        allInfoCache.put("topics", topics);
        List<Long> topicIds = topics.stream().map(Topic::getId).collect(Collectors.toList());
        List<Task> tasks = taskRepository.findByVisibleIsTrueAndTopicIdIn(topicIds);
        allInfoCache.put("tasks", tasks);
        List<Long> taskIds = tasks.stream().map(Task::getId).collect(Collectors.toList());
        List<Assignment> assignments = assignmentRepository.findByTaskIds(taskIds);
        allInfoCache.put("assignments", assignments);
        List<Long> assignmentIds = assignments.stream().map(Assignment::getId).collect(Collectors.toList());
        List<AssignmentQuiz> assignmentQuizzes = assignmentQuizRepository.findByAssignmentIdInOrderByOrderNumber(assignmentIds);
        allInfoCache.put("assignmentQuizzes", assignmentQuizzes);

        List quizzesAndAnswer = assignments.stream().map(assignment -> {
            List<AssignmentQuiz> findAssignmentQuiz = assignmentQuizzes.stream().filter(assignmentQuiz -> assignmentQuiz.getAssignmentId().equals(assignment.getId())).collect(Collectors.toList());
            String quizIds = findAssignmentQuiz.stream()
                    .map(assignmentQuiz -> assignmentQuiz.getQuizId().toString())
                    .collect(Collectors.joining(","));
            List quizzesAndAnswerList = quizCenterService.getQuizzesAndAnswerByQuizIds(quizIds, assignment.getId(), userId, assignment.getType());
            Map<String, Object> quizzesAndAnswerMap = new HashMap<>();
            quizzesAndAnswerMap.put("assignmentId", assignment.getId());
            quizzesAndAnswerMap.put("type", assignment.getType());
            quizzesAndAnswerMap.put("quizzesAndAnswer", quizzesAndAnswerList);
            return quizzesAndAnswerMap;
        }).collect(Collectors.toList());
        allInfoCache.put("quizzesAndAnswerList", quizzesAndAnswer);
        return allInfoCache;
    }
}
